﻿using Als.Common.String;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Als.Common.Extensions
{
    /// <summary>
    /// 字符串<see cref="String"/>类型的扩展辅助操作类
    /// </summary>
    public static class StringExtensions
    {
        #region 正则表达式

        /// <summary>
        /// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项
        /// </summary>
        /// <param name="value">要搜索匹配项的字符串</param>
        /// <param name="pattern">要匹配的正则表达式模式</param>
        /// <returns>如果正则表达式找到匹配项，则为 true；否则，为 false</returns>
        public static bool IsMatch(this string value, string pattern)
        {
            if (value == null)
            {
                return false;
            }
            return Regex.IsMatch(value, pattern);
        }

        /// <summary>
        /// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项
        /// </summary>
        /// <param name="value">要搜索匹配项的字符串</param>
        /// <param name="pattern">要匹配的正则表达式模式</param>
        /// <returns>一个对象，包含有关匹配项的信息</returns>
        public static string Match(this string value, string pattern)
        {
            if (value == null)
            {
                return null;
            }
            return Regex.Match(value, pattern).Value;
        }

        /// <summary>
        /// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合
        /// </summary>
        /// <param name="value"> 要搜索匹配项的字符串 </param>
        /// <param name="pattern"> 要匹配的正则表达式模式 </param>
        /// <returns> 一个集合，包含有关匹配项的字符串值 </returns>
        public static IEnumerable<string> Matches(this string value, string pattern)
        {
            if (value == null)
            {
                return new string[] { };
            }
            MatchCollection matches = Regex.Matches(value, pattern);
            return from Match match in matches select match.Value;
        }

        /// <summary>
        /// 是否电子邮件
        /// </summary>
        public static bool IsEmail(this string value)
        {
            const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否是IP地址
        /// </summary>
        public static bool IsIpAddress(this string value)
        {
            const string pattern = @"^(\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d\.){3}\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 邮编有效性
        /// </summary>
        /// <param name="zip">输入字符串</param>
        /// <returns>返回值</returns>
        public static bool IsZipCode(this string zip)
        {
            var rx = new Regex(@"^\d{6}$", RegexOptions.None);
            Match m = rx.Match(zip);
            return m.Success;
        }

        /// <summary>
        /// 是否是整数
        /// </summary>
        public static bool IsNumeric(this string value)
        {
            const string pattern = @"^\-?[0-9]+$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否是Unicode字符串
        /// </summary>
        public static bool IsUnicode(this string value)
        {
            const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否Url字符串
        /// </summary>
        public static bool IsUrl(this string value)
        {
            const string pattern = @"^(http|https|ftp|rtsp|mms):(\/\/|\\\\)[A-Za-z0-9%\-_@]+\.[A-Za-z0-9%\-_@]+[A-Za-z0-9\.\/=\?%\-&_~`@:\+!;]*$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否身份证号，验证如下3种情况：
        /// 1.身份证号码为15位数字；
        /// 2.身份证号码为18位数字；
        /// 3.身份证号码为17位数字+1个字母
        /// </summary>
        public static bool IsIdentityCard(this string value)
        {
            const string pattern = @"^(^\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否手机号码
        /// </summary>
        /// <param name="value"></param>
        /// <param name="isRestrict">是否按严格格式验证</param>
        public static bool IsMobileNumber(this string value, bool isRestrict = false)
        {
            string pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 判断是否为base64字符串
        /// </summary>
        /// <param name="str">输入字符串</param>
        /// <returns>返回值</returns>
        public static bool IsBase64(this string str)
        {
            return Regex.IsMatch(str, @"[A-Za-z0-9\+\/\=]");
        }

        /// <summary>
        /// 验证字符串是否是GUID
        /// </summary>
        /// <param name="guid">字符串</param>
        /// <returns>返回值</returns>
        public static bool IsGuid(this string guid)
        {
            //if (string.IsNullOrEmpty(guid))
            //    return false;

            //return Regex.IsMatch(guid, "[A-F0-9]{8}(-[A-F0-9]{4}){3}-[A-F0-9]{12}|[A-F0-9]{32}", RegexOptions.IgnoreCase);
            Guid nguid;
            return Guid.TryParse(guid, out nguid);
        }

        /// <summary>
        /// 是否是int
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值 bool</returns>
        public static bool IsInt(this string input)
        {
            return Regex.IsMatch(input, @"^[1-9]\d*\.?[0]*$");
        }

        /// <summary>
        /// 是否数字字符串(0~9的字符串)
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值 bool</returns>
        public static bool IsNum(this string input)
        {
            Regex regNumber = new Regex("^[0-9]+$");
            Match m = regNumber.Match(input);
            return m.Success;
        }

        /// <summary>
        /// 是否数字字符串 可带正负号
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值 bool</returns>
        public static bool IsNumberSign(this string input)
        {
            Regex regNumberSign = new Regex("^[+-]?[0-9]+$");
            Match m = regNumberSign.Match(input);
            return m.Success;
        }

        /// <summary>
        /// 是否是浮点数
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值</returns>
        public static bool IsDecimal(this string input)
        {
            Regex regDecimal = new Regex("^[0-9]+[.]?[0-9]+$");
            Match m = regDecimal.Match(input);
            return m.Success;
        }

        /// <summary>
        /// 是否是浮点数 可带正负号
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值</returns>
        public static bool IsDecimalSign(this string input)
        {
            Regex regDecimalSign = new Regex("^[+-]?[0-9]+[.]?[0-9]+$"); //等价于^[+-]?\d+[.]?\d+$
            Match m = regDecimalSign.Match(input);
            return m.Success;
        }

        #endregion

        #region 返回 整数(int32)

        /// <summary>
        /// 返回 整数(int32)
        /// </summary>
        /// <param name="str"></param>
        /// <param name="state">状态 true 表示成功 false 表示失败</param>
        /// <returns></returns>
        public static int ToInt32(this string str, out bool state)
        {
            var result = 0;
            state = int.TryParse(str, out result);
            return result;
        }

        #endregion

        #region 返回整数（int64）

        /// <summary>
        /// 返回整数（int64）
        /// </summary>
        /// <param name="str"></param>
        /// <param name="state">状态 true 表示成功 false 表示失败</param>
        /// <returns></returns>
        public static long ToInt64(this string str, out bool state)
        {
            long result = 0;
            state = long.TryParse(str, out result);
            return result;
        }

        #endregion

        #region TrimEnd 末尾需要去除的字符串
        /// <summary>
        /// 扩展EndTrim
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="endstr">末尾需要去除的字符串</param>
        /// <returns></returns>
        public static string TrimEnd(this string str, string endstr)
        {
            var chars = new char[endstr.Trim().Length];
            for (var i = 0; i < endstr.Trim().Length; i++)
            {
                chars[i] = Convert.ToChar(endstr.Trim().Substring(i, 1));
            }
            return str.TrimEnd(chars);
        }
        #endregion

        #region TrimStart 字符串开始需要去除的字符串
        /// <summary>
        /// 扩展EndTrim
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="startstr">字符串开始需要去除的字符串</param>
        /// <returns></returns>
        public static string TrimStart(this string str, string startstr)
        {
            var chars = new char[startstr.Trim().Length];
            for (var i = 0; i < startstr.Trim().Length; i++)
            {
                chars[i] = Convert.ToChar(startstr.Trim().Substring(i, 1));
            }
            return str.TrimStart(chars);
        }
        #endregion

        #region ToString  把 List<T> 按照分隔符组装成 string
        /// <summary>
        /// 把 List<T> 按照分隔符组装成 string
        /// </summary>
        /// <param name="list"></param>
        /// <param name="speater"></param>
        /// <returns></returns>
        public static string ToString<T>(this List<T> list, string speater)
        {
            var sb = new StringBuilder();
            for (var i = 0; i < list.Count; i++)
            {
                sb.Append(i == list.Count - 1 ? list[i].ToString() : $"{list[i]}{speater}");
            }
            return sb.ToString();
        }
        #endregion

        #region 检查字符串最大长度，返回指定长度的串

        /// <summary>
        /// 检查字符串最大长度，返回指定长度的串
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <param name="maxLength">最大长度</param>
        /// <returns>返回值</returns>			
        public static string Split(this string input, int maxLength)
        {
            if (!string.IsNullOrEmpty(input))
            {
                input = input.Trim();
                if (input.Length > maxLength) //按最大长度截取字符串
                {
                    input = input.Substring(0, maxLength);
                }
            }
            return input;
        }

        #endregion

        #region  ToSBC 转全角的函数(SBC case)  
        /// <summary>  
        /// 转全角的函数(SBC case)  
        /// </summary>  
        /// <param name="str"></param>  
        /// <returns></returns>  
        public static string ToSbc(this string str)
        {
            string a = "";
            //半角转全角：  
            char[] c = str.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 32)
                {
                    c[i] = (char)12288;
                    continue;
                }
                if (c[i] < 127)
                    c[i] = (char)(c[i] + 65248);
            }
            return new string(c);
        }
        #endregion

        #region ToDBC 转半角的函数(SBC case)  
        /// <summary>  
        ///  转半角的函数(SBC case)  
        /// </summary>  
        /// <param name="str">输入</param>  
        /// <returns></returns>  
        public static string ToDbc(this string str)
        {
            char[] c = str.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 12288)
                {
                    c[i] = (char)32;
                    continue;
                }
                if (c[i] > 65280 && c[i] < 65375)
                    c[i] = (char)(c[i] - 65248);
            }
            return new string(c);
        }
        #endregion

        #region 中文检测

        /// <summary>
        /// 检测是否有中文字符
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>返回值</returns>
        public static bool IsHasChzn(this string input)
        {
            Regex regChzn = new Regex("[\u4e00-\u9fa5]");
            Match m = regChzn.Match(input);
            return m.Success;
        }
        #endregion

        #region 其他操作

        /// <summary>
        /// 指示指定的字符串是 null 还是 System.String.Empty 字符串
        /// </summary>
        public static bool IsNullOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }

        /// <summary>
        /// 指示指定的字符串是 null、空还是仅由空白字符组成。
        /// </summary>
        public static bool IsNullOrWhiteSpace(this string value)
        {
            return string.IsNullOrWhiteSpace(value);
        }

        /// <summary>
        /// 为指定格式的字符串填充相应对象来生成字符串
        /// </summary>
        /// <param name="format">字符串格式，占位符以{n}表示</param>
        /// <param name="args">用于填充占位符的参数</param>
        /// <returns>格式化后的字符串</returns>
        public static string FormatWith(this string format, params object[] args)
        {
            return format.IsNotNull() ? string.Format(CultureInfo.CurrentCulture, format, args) : "";
        }

        /// <summary>
        /// 将字符串反转
        /// </summary>
        /// <param name="value">要反转的字符串</param>
        public static string ReverseString(this string value)
        {
            return value.IsNotNull() ? new string(value.Reverse().ToArray()) : "";
        }

        /// <summary>
        /// 判断指定路径是否图片文件
        /// </summary>
        public static bool IsImageFile(this string filename)
        {
            if (!System.IO.File.Exists(filename))
            {
                return false;
            }
            byte[] filedata = System.IO.File.ReadAllBytes(filename);
            if (filedata.Length == 0)
            {
                return false;
            }
            ushort code = BitConverter.ToUInt16(filedata, 0);
            switch (code)
            {
                case 0x4D42: //bmp
                case 0xD8FF: //jpg
                case 0x4947: //gif
                case 0x5089: //png
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// 以指定字符串作为分隔符将指定字符串分隔成数组
        /// </summary>
        /// <param name="value">要分割的字符串</param>
        /// <param name="strSplit">字符串类型的分隔符</param>
        /// <param name="removeEmptyEntries">是否移除数据中元素为空字符串的项</param>
        /// <returns>分割后的数据</returns>
        public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false)
        {
            return value.Split(new[] { strSplit }, removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
        }

        /// <summary>
        /// 获取字符串的MD5 Hash值
        /// </summary>
        public static string ToMd5Hash(this string value)
        {
            return value.GetMD5_32();
        }

        /// <summary>
        /// 支持汉字的字符串长度，汉字长度计为2
        /// </summary>
        /// <param name="value">参数字符串</param>
        /// <returns>当前字符串的长度，汉字长度为2</returns>
        public static int TextLength(this string value)
        {
            ASCIIEncoding ascii = new ASCIIEncoding();
            int tempLen = 0;
            byte[] bytes = ascii.GetBytes(value);
            foreach (byte b in bytes)
            {
                if (b == 63)
                {
                    tempLen += 2;
                }
                else
                {
                    tempLen += 1;
                }
            }
            return tempLen;
        }

        /// <summary>
        /// 将JSON字符串还原为对象
        /// </summary>
        /// <typeparam name="T">要转换的目标类型</typeparam>
        /// <param name="json">JSON字符串 </param>
        /// <returns></returns>
        public static T FromJsonString<T>(this string json)
        {
            return json.IsNotNull() ? JsonConvert.DeserializeObject<T>(json) : default(T);
        }

        /// <summary>
        /// 将字符串转换为<see cref="byte"/>[]数组，默认编码为<see cref="Encoding.UTF8"/>
        /// </summary>
        public static byte[] ToBytes(this string value, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            return encoding.GetBytes(value);
        }

        /// <summary>
        /// 将<see cref="byte"/>[]数组转换为字符串，默认编码为<see cref="Encoding.UTF8"/>
        /// </summary>
        public static string ToString(this byte[] bytes, Encoding encoding)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            return encoding.GetString(bytes);
        }

        #endregion
    }
}
